﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using RainEngine.SceneNodes;


namespace RainEngine.Controllers
{
    /// <summary>
    /// 控制一个相机对象。
    /// 使用鼠标控制相机上下左右旋转，键盘控制前进后退左右平移。
    /// 相机不能翻滚，只在水平面xz上移动。
    /// </summary>
    public class FpsCameraController : Controller
    {
        #region 构造函数和成员变量
        
        /// <summary>
        /// 要控制的相机。
        /// </summary>
        private CameraSceneNode camera;

        /// <summary>
        /// 光标的初始位置，位于屏幕中央，每一帧都会将鼠标放置到屏幕中央。
        /// </summary>
        private MouseState originalMouseState;
        
        /// <summary>
        /// 相机允许俯仰的最大角度。
        /// </summary>
        private float elevation = 30;

        /// <summary>
        /// 创建一个FpsCameraController对象。
        /// </summary>
        /// <param name="engine">引擎</param>
        public FpsCameraController(RainGE engine): base(engine)
        {
            
        }
        #endregion

        /// <summary>
        /// 获取或设置相机允许俯仰的最大角度。
        /// </summary>
        public float Elevation
        {
            get { return elevation; }
            set { elevation = value; }
        }
        
        /// <summary>
        /// 主控制逻辑
        /// </summary>
        /// <param name="gameTime"></param>
        public override void ControllerAction(GameTime gameTime)
        {
            if (camera == null)
            {
                camera = (CameraSceneNode)controlledNode;
                //将鼠标初始位置设置为屏幕中央
                //将鼠标初始位置设置为屏幕中央
                Mouse.SetPosition(engine.GraphicsDevice.Viewport.Width / 2, engine.GraphicsDevice.Viewport.Height / 2);
                originalMouseState = Mouse.GetState();            
            }
            float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
            ProcessInput(timeDifference);
        }

        private void ProcessInput(float ellapsedTime)
        {
            //相机的上下旋转
            float cameraPitch = 0;
            //相机的左右旋转
            float cameraYaw = 0;
            //前进后退
            float movement = 0;
            //左右平移
            float stride = 0;
            //相机每帧旋转和移动距离
            float rotationPerFrame = ellapsedTime * camera.RotationSpeed;
            float movementPerFrame = ellapsedTime * camera.MovementSpeed;

            if (engine.IsActive)
            {
                //获取鼠标的当前状态
                MouseState currentMouseState = Mouse.GetState();
                
                //如果鼠标发生移动，则通过鼠标控制相机的俯仰和左右旋转
                if (currentMouseState != originalMouseState)
                {
                    //获取鼠标在水平和竖直方向的移动大小
                    float xDifference = currentMouseState.X - originalMouseState.X;
                    float yDifference = currentMouseState.Y - originalMouseState.Y;
                    
                    //水平方向的移动控制相机的左右旋转
                    cameraYaw += rotationPerFrame * xDifference;
                    //竖直方向的移动控制相机的上下旋转
                    cameraPitch -= rotationPerFrame * yDifference;
                    
                    //将光标重新设置到屏幕的中央
                    Mouse.SetPosition(originalMouseState.X, originalMouseState.Y);
                }
            }

            //键盘控制相机的前后左右移动
            if (Input.KeyboardUpPressed|| Input.Keyboard .IsKeyDown (Keys.W))
                movement += movementPerFrame;
            if (Input.KeyboardDownPressed|| Input.Keyboard .IsKeyDown(Keys.S))
                movement -= movementPerFrame;
            if (Input.KeyboardRightPressed|| Input.Keyboard.IsKeyDown(Keys.D))
                stride += movementPerFrame;
            if (Input.KeyboardLeftPressed || Input.Keyboard.IsKeyDown(Keys.A))
                stride -= movementPerFrame;            

            //如果相机旋转或移动
            if (cameraYaw != 0 || cameraPitch != 0 || movement != 0 || stride != 0)
            {
                //-------------------------
                //  当相机旋转时                        
                //-------------------------
                if (cameraYaw != 0 || cameraPitch != 0)
                {
                    //isDirty设为true表示需要更新相机
                    isDirty = true;
                    
                    //将相机的俯仰限制在elevation范围之内
                    if (Math.Abs(cameraPitch + camera.AngleRotation.X) > elevation)
                        cameraPitch = Math.Sign(cameraPitch) * (elevation - Math.Abs(camera.AngleRotation.X));
                    
                    //基于cameraPitch, cameraYaw设置相机的旋转。
                    if (cameraYaw != 0 || cameraPitch != 0)
                    {
                        Vector3 angleRotation = new Vector3(cameraPitch, cameraYaw, 0);
                        camera.AngleRotation += angleRotation;
                        camera.Pose.Rotation = Quaternion.CreateFromYawPitchRoll(MathHelper.ToRadians(-camera.AngleRotation.Y), MathHelper.ToRadians(camera.AngleRotation.X), 0);
                    }
                }

                //--------------------
                //  当相机前后移动时
                //--------------------
                if (movement != 0)
                {
                    //isDirty设为true表示需要更新相机
                    isDirty = true;

                    //计算前进后退的矢量
                    Vector3 forwardMove = camera.GetMoveForwardVector(movement);
                    camera.Pose.Position += forwardMove;                    
                }

                //--------------------
                //  当相机左右移动时
                //--------------------
                if (stride != 0)
                {
                    //isDirty设为true表示需要更新相机
                    isDirty = true;
                    
                    //计算左右平移的矢量
                    Vector3 strideMove = CameraSceneNode.GetMoveVector(Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.ToRadians(-camera.AngleRotation.Y)), Vector3.Right, stride*0.5f);
                    camera.Pose.Position += strideMove;
                }

                //更新相机
                if (isDirty)
                {                    
                    camera.UpdateViewMatrix(true);
                    isDirty = false;
                }
            } 
        }
    }
}
